home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Just Call Me Internet
/
Just Call Me Internet.iso
/
prog
/
atari
/
c
/
snz128s
/
src
/
snews2.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-06-02
|
38KB
|
1,644 lines
/*
SNEWS 2.0
snews - a simple threaded news reader
Copyright (C) 1991 John McCombs, Christchurch, NEW ZEALAND
john@ahuriri.gen.nz
PO Box 2708, Christchurch, NEW ZEALAND
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License, version 1, as
published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
See the file COPYING, which contains a copy of the GNU General
Public License.
Atari version ported by Graham Judd - gjudd@siward.demon.co.uk
*/
/*---------------------------- Source Control ------------------------------*/
/*
* $Id: snews2.c,v 1.2 1994/02/05 18:52:38 gbj Exp user $
*/
/****************************************************************************
* 20 May 92 1.2 GT ka9q mods. *
* 22 May 92 1.3 GT Set time zone. *
* 05 Jun 92 1.4 GT Nikki Locke's SPACE command. *
* 06 Jun 92 1.5 GT Invalidate freed pointers. *
* 09 Jun 92 1.6 GT Right and left cursor keys. *
* 12 Jun 92 1.7 NJL Add v_init call & change to use scr_lines. *
* Add 'q' as alternative to ESC throughout. *
* Fix memory leak when Escaping from read_thread. *
* Ensure strtok() accepts tab as well as space. *
* 17 Jul 92 1.8 GT C++ compilation. *
* Heap debugging. *
* Swap out when executing child process. *
* 16 Aug 92 1.9 MSM Snews 1.9 *
* Lock history etc. during use *
* 31 May 93 1.10 MSM Snews 2.0 *
* 18 Jun 93 1.11 MSM Heap debugging removed (using Bounds Check) *
* 10 Jul 93 1.12 MSM New commands added, help updated *
* 7 Aug 93 1.13 MSM Change command functionality to be TINish *
* MSM Add expert mode *
* MSM Add confirm exit mode *
* 26 Aug 93 1.13 MSM Colour Support clean up, Expert toggle, bugs *
* 3 Oct 92 1.14 MSM Case sensitive y/n questions made insensitive *
* old style tab action added, new config options *
* 132 col / 43 line support corrected *
* article/thread save function corrected. *
* 24 Nov 93 1.15 MSM Changed to Delta version control *
* Session setting text changes *
* 25 Nov 93 1.16 MSM Correct read list processing *
* 5 Feb 94 AT2 GBJ Atari port. *
* 2 Apr 94 1.17 MSM Add print thread support *
* abort/replace/append choice for thread save *
* 15.89 style added to smart match *
* Save full newsgroup *
* Suspend support *
*****************************************************************************/
#include <time.h>
#include <io.h>
#include <fcntl.h>
#include "defs.h"
#include "snews.h"
#include "screen.h"
#ifdef ATARI
# include <sys/types.h>
# include "st.h"
# include "fileops.h"
#else
# include "exec.h"
#endif
#include "locking.h"
#ifndef __TURBOC__
#ifndef ATARI
#include <graph.h>
#include <dos.h>
#define BLACK 0
#define LIGHTGRAY 7
unsigned long farcoreleft(void);
#endif
#endif
extern int xfile;
extern INFO my_stuff;
#ifdef ATARI
extern unsigned long _STACK;
#else
extern unsigned _stklen;
#endif
extern char search_text[];
extern char save_name[];
extern char search_group[];
/*------------------------- find which thread to read ----------------------*/
int select_thread(ACTIVE * gp, ARTICLE * head)
{
/*
* Present the list of threads, and allow him to move up and down with
* the arrow and PgUp and PgDn keys.
*/
ARTICLE *top; /* thread at the top of the page */
ARTICLE *this_thread; /* current thread */
ARTICLE *th;
ART_ID *art;
int exit_code; /* why we are exiting the loop */
char sub_tmp[80];
int ch, i, idx, hit, a_ct, ch2;
this_thread = head;
top = head;
exit_code = 0;
show_threads(gp, &top, this_thread, TRUE, head);
while (exit_code == 0) {
ch = getch();
switch (ch) {
case 0:
case 0xE0:
ch = getch();
switch (ch) {
case Fn1:
show_help(HELP_THREAD);
show_threads(gp, &top, this_thread, TRUE, head);
break;
case Fn2:
show_values();
show_threads(gp, &top, this_thread, TRUE, head);
break;
case Fn3:
change_values();
show_threads(gp, &top, this_thread, TRUE, head);
break;
case UP_ARR:
if (this_thread->last != NULL)
this_thread = this_thread->last;
break;
case DN_ARR:
if (this_thread->next != NULL)
this_thread = this_thread->next;
break;
case PGUP:
for (i = this_thread->index - top->index; i < PAGE_LENGTH; i++) {
if (this_thread->last == NULL)
break;
this_thread = this_thread->last;
}
break;
case PGDN:
for (i = this_thread->index - top->index; i < PAGE_LENGTH; i++) {
if (this_thread->next == NULL)
break;
this_thread = this_thread->next;
}
break;
case HOME:
top = this_thread = head;
show_threads(gp, &top, this_thread, TRUE, head);
break;
case END:
top = this_thread = head;
while (this_thread->next != NULL)
this_thread = this_thread->next;
break;
case RT_ARR:
read_thread(gp, this_thread, this_thread->art_num, 1);
show_threads(gp, &top, this_thread, TRUE, head);
break;
case LT_ARR:
exit_code = EX_QUIT;
break;
}
break;
case 'k':
if (this_thread->last != NULL)
this_thread = this_thread->last;
break;
case 'j':
if (this_thread->next != NULL)
this_thread = this_thread->next;
break;
case 2:
case 21:
case 'b':
for (i = 0; i < PAGE_LENGTH; i++) {
if (this_thread->last == NULL)
break;
this_thread = this_thread->last;
}
break;
case 4:
case 6:
case ' ':
for (i = 0; i < PAGE_LENGTH; i++) {
if (this_thread->next == NULL)
break;
this_thread = this_thread->next;
}
break;
case '!':
textbackground(headb);
textcolor(headf);
clrscr();
#ifdef ATARI
printf("Sorry, function not supported. \n");
printf("Press a key to return to Snews.\n\n");
getch();
#else
printf("Type <EXIT> to return to Snews.\n\n");
system("");
#endif
textbackground(textb);
textcolor(textf);
clrscr();
show_threads(gp, &top, this_thread, TRUE, head);
break;
case 's':
xfile = FALSE;
save_thread_to_disk(gp, this_thread);
getch();
show_threads(gp, &top, this_thread, TRUE, head);
break;
case 'S':
xfile = TRUE;
save_thread_to_disk(gp, this_thread);
xfile = FALSE;
getch();
show_threads(gp, &top, this_thread, TRUE, head);
break;
case 'M':
mail_to_someone(NULL);
show_threads(gp, &top, this_thread, TRUE, head);
break;
case 'v':
#ifdef ATARI
sprintf(sub_tmp, "Demon Internet Simple News v%02d.%02d.%02d LC5 compiled %s", rmj, rmm, rup, __DATE__);
#else
#ifdef __TURBOC__
sprintf(sub_tmp, "Demon Internet Simple News v%02d.%02d.%02d BC++ compiled %s", rmj, rmm, rup, __DATE__);
#else
sprintf(sub_tmp, "Demon Internet Simple News v%02d.%02d.%02d MSVC compiled %s", rmj, rmm, rup, __DATE__);
#endif
#endif
message(sub_tmp);
getch();
show_threads(gp, &top, this_thread, TRUE, head);
break;
case 'w':
strcpy(sub_tmp, "");
if (gp->suspend == TRUE) {
message("This group is suspended, post regardless ? (y/n) ");
ch2 = getch();
ch2 = tolower(ch2);
message("");
}
else
ch2 = 'y';
if (ch2 == 'y') {
post(NULL, gp->group, sub_tmp);
show_threads(gp, &top, this_thread, TRUE, head);
}
break;
case 'o':
print_thread(gp, this_thread);
message("-- Done --");
break;
case 'h':
case 'H':
show_help(HELP_THREAD);
show_threads(gp, &top, this_thread, TRUE, head);
break;
case TAB:
case 'N':
if (my_stuff.tab_action == TRUE) {
/*
* Go to the next unread article. Work through each
* thread, looking at each article to see if it's been
* read
*/
/* for each thread */
th = this_thread;
hit = FALSE;
while (th != NULL) {
art = th->art_num;
a_ct = 0;
/* for each article */
while (art != NULL) {
idx = (int) (art->id - gp->lo_num - 1);
a_ct++;
if (*((gp->read_list) + idx) == FALSE) {
hit = TRUE;
break;
}
art = art->next_art;
}
if (hit)
break;
th = th->next;
}
if (hit) {
this_thread = th;
read_thread(gp, this_thread, art, a_ct);
show_threads(gp, &top, this_thread, TRUE, head);
}
else {
exit_code = EX_NEXT;
}
break;
}
else { /* old style tab action */
/*
* Go to the next unread article. Work through each
* thread, looking at each article to see if it's been
* read
*/
/* for each thread */
th = this_thread;
hit = FALSE;
while (th != NULL) {
art = th->art_num;
a_ct = 0;
/* for each article */
while (art != NULL) {
idx = (int)(art->id - gp->lo_num - 1);
a_ct++;
if ( *((gp->read_list)+idx) == FALSE) {
hit = TRUE;
break;
}
art = art->next_art;
}
if (hit) break;
th = th->next;
}
if (hit) {
this_thread = th;
read_thread(gp, this_thread, art, a_ct);
show_threads(gp, &top, this_thread, TRUE, head);
} else {
message("-- No more articles to read --");
}
break;
}
case 'P':
/*
* Go to the previous unread article. Work through each
* thread, looking at each article to see if it's been
* read
*/
/* for each thread */
th = this_thread->last;
hit = FALSE;
while (th != NULL) {
art = th->art_num;
a_ct = 0;
/* for each article */
while (art != NULL) {
idx = (int) (art->id - gp->lo_num - 1);
a_ct++;
if (*((gp->read_list) + idx) == FALSE) {
hit = TRUE;
break;
}
art = art->next_art;
}
if (hit)
break;
th = th->last;
}
if (hit) {
this_thread = th;
read_thread(gp, this_thread, art, a_ct);
show_threads(gp, &top, this_thread, TRUE, head);
}
else {
message("-- No previous articles unread --");
}
break;
case 'X':
for(th=head;th!=this_thread;th=th->next)
mark_thread_as_read(gp, th, TRUE);
show_threads(gp, &top, this_thread, TRUE, head);
break;
case 'K':
mark_thread_as_read(gp, this_thread, FALSE);
th = this_thread;
hit = FALSE;
while (th != NULL) {
art = th->art_num;
a_ct = 0;
/* for each article */
while (art != NULL) {
idx = (int) (art->id - gp->lo_num - 1);
a_ct++;
if (*((gp->read_list) + idx) == FALSE) {
hit = TRUE;
break;
}
art = art->next_art;
}
if (hit)
break;
th = th->next;
}
if (th != NULL)
this_thread = th;
show_threads(gp, &top, this_thread, TRUE, head);
if (th == NULL)
message("-- No more articles to read --");
break;
case 'C':
if (mark_group_as_read(gp, TRUE) == TRUE)
exit_code = EX_NEXT;
else
lmessage("");
break;
case 'c':
if (mark_group_as_read(gp, TRUE) == TRUE)
show_threads(gp, &top, this_thread, TRUE, head);
else
lmessage("");
break;
case 'z':
case 'Z':
mark_thread_as_unread(gp, this_thread);
show_threads(gp, &top, this_thread, TRUE, head);
break;
case ENTER:
read_thread(gp, this_thread, this_thread->art_num, 1);
show_threads(gp, &top, this_thread, TRUE, head);
break;
case BACKSP:
art = this_thread->art_num;
a_ct =1;
while (art->next_art != NULL) {
a_ct++;
art = art->next_art;
}
read_thread(gp, this_thread, art, a_ct);
show_threads(gp, &top, this_thread, TRUE, head);
break;
case '+':
case '=':
case '/':
this_thread = search_thread(gp, this_thread, ch=='/', TRUE);
show_threads(gp, &top, this_thread, TRUE, head);
break;
case '-':
case '?':
this_thread = search_thread(gp, this_thread, ch=='?', FALSE);
show_threads(gp, &top, this_thread, TRUE, head);
break;
case 'B':
bug_report();
show_threads(gp, &top, this_thread, TRUE, head);
break;
case ESCAPE:
case 'q':
exit_code = EX_QUIT;
break;
};
if (exit_code == 0)
show_threads(gp, &top, this_thread, FALSE, head);
}
return exit_code;
}
/*------------------------ mark thread as read ------------------------------*/
int mark_thread_as_read(ACTIVE *gp, ARTICLE *this_thread, int q)
{
ART_ID *id;
int idx, ch;
if ((q == 0) && (my_stuff.expert != TRUE)) {
message("Mark all articles in thread as read (y/n) [y] ? ");
do
{
ch = getch();
ch = tolower(ch);
}
while ((ch != 'y') && (ch != 'n') && (ch != ESCAPE) && (ch != '\r'));
}
else
ch = 'y';
if ((ch == 'y') || (ch == '\r')) {
id = this_thread->art_num;
while (id != NULL) {
/* mark this article as read */
idx = (int) ((id->id) - gp->lo_num - 1);
*((gp->read_list) + idx) = (char) TRUE;
id = id->next_art;
}
message("");
return TRUE;
}
message("");
return FALSE;
}
/*------------------------ mark thread as unread ------------------------------*/
void mark_thread_as_unread(ACTIVE *gp, ARTICLE *this_thread)
{
ART_ID *id;
int idx, ch;
if (my_stuff.expert != TRUE) {
message("Mark all articles in thread as unread (y/n) [y] ? ");
do
{
ch = getch();
ch = tolower(ch);
}
while ((ch != 'y') && (ch != 'n') && (ch != ESCAPE) && (ch != '\r'));
}
else
ch = 'y';
if ((ch == 'y') || (ch == '\r')) {
id = this_thread->art_num;
while (id != NULL) {
/* mark this article as read */
idx = (int) ((id->id) - gp->lo_num - 1);
*((gp->read_list) + idx) = (char) FALSE;
id = id->next_art;
}
}
message("");
}
/*------------------------ save a newsgroup ------------------------------*/
void save_group_to_disk(ACTIVE * gp)
{
char fnx[80];
FILE *tmp, *tmp_file;
char *fn, ch;
char buf[512], prompt[80];
sprintf(prompt, "Enter filename [%s] ", save_name);
lmessage(prompt);
gets(fnx);
if (strlen(fnx) > 0)
strcpy(save_name, fnx);
else
strcpy(fnx, save_name);
tmp = NULL;
if (access(fnx, 0) == 0) {
message("File exists - append/replace/exit (a/r/e)? ");
do {
ch = getch();
ch = tolower(ch);
} while ((ch != 'a') && (ch != 'e') && (ch != 'r'));
if (ch == 'e') {
message("*** Save aborted - press any key to continue ***");
getch();
return;
}
if (ch == 'a') {
if ((tmp = fopen(fnx, "at")) == NULL) {
message("*** Cannot open file for appending - "
"press any key to continue ***");
getch();
}
}
else {
if ((tmp = fopen(fnx, "wt")) == NULL) {
message("*** Cannot open file for output - "
"press any key to continue ***");
getch();
}
}
}
else {
if ((tmp = fopen(fnx, "wt")) == NULL) {
message("*** Cannot open file for output - "
"press any key to continue ***");
getch();
}
}
if (tmp != NULL) {
message("*** Saving Article(s) - please wait ***");
fn = make_news_group_name(gp->group);
if ((tmp_file = fopen(fn, "rb")) != NULL) {
while (fgets(buf, sizeof(buf), tmp_file) != NULL) {
if (strncmp(buf, "@@@@END\n", 7) == 0)
fputc(0x0c, tmp);
else
fputs(buf, tmp);
}
fclose(tmp_file);
}
fclose(tmp);
message("Article(s) saved - press any key to continue");
getch();
}
}
/*------------------------ save a thread ------------------------------*/
void save_thread_to_disk(ACTIVE * gp, ARTICLE * this_thread)
{
ART_ID *id;
char *fn;
int a_ct;
char fnx[80];
int ch;
FILE *tmp, *tmp_file;
time_t now;
struct tm *tmnow;
char timestr[64];
char buf[512], prompt[128];
if (xfile == TRUE) { /* extract function */
strcpy(fnx, my_stuff.mail_dir);
strcat(fnx, my_stuff.extruser);
strcat(fnx, ".txt");
}
else {
sprintf(prompt, "Enter filename? [%s] ", save_name);
lmessage(prompt);
gets(fnx);
if (strlen(fnx) > 0)
strcpy(save_name, fnx);
else
strcpy(fnx, save_name);
}
tmp = NULL;
if (xfile == TRUE) { /* extract function */
if (access(fnx, 0) == 0) {
if ((tmp = fopen(fnx, "at")) == NULL) {
message("*** Cannot open file for appending -"
"press any key to continue ***");
getch();
}
}
else {
if ((tmp = fopen(fnx, "wt")) == NULL) {
message("*** Cannot open file for output - press a key to continue ***");
getch();
}
}
}
else { /* save function */
if (access(fnx, 0) == 0) {
message("File exists - append/replace/exit(a/r/e)? ");
do
{
ch = getch();
ch = tolower(ch);
}
while ((ch != 'a') && (ch != 'e') && (ch != 'r'));
if (ch == 'e') {
message("*** Save aborted - press any key to continue ***");
getch();
return;
}
if (ch == 'a') {
if ((tmp = fopen(fnx, "at")) == NULL) {
message("*** Cannot open file for appending - "
"please any key to continue ***");
getch();
}
}
else {
if ((tmp = fopen(fnx, "wt")) == NULL) {
message("*** Cannot open file for output - press a key to continue ***");
getch();
}
}
}
else {
if ((tmp = fopen(fnx, "wt")) == NULL) {
message("*** Cannot open file for output - press a key to continue ***");
getch();
}
}
}
if (tmp != NULL) {
message("*** Saving Article(s) - please wait ***");
fn = make_news_group_name(gp->group);
id = this_thread->art_num;
a_ct = 0;
while (id != NULL) {
if (xfile == TRUE) {
tzset();
time(&now);
tmnow = localtime(&now);
strftime(timestr, sizeof(timestr), "%a %b %d %H:%M:%S %Z %Y", tmnow);
fprintf(tmp, "From snews@%s.%s %s\n", my_stuff.my_site,
my_stuff.my_domain, timestr);
}
if ((tmp_file = fopen(fn, "rb")) != NULL) {
fseek(tmp_file, id->art_off, SEEK_SET);
while (fgets(buf, sizeof(buf), tmp_file) != NULL) {
if (strncmp(buf, "@@@@END\n", 7) == 0)
break;
fputs(buf, tmp);
}
fclose(tmp_file);
}
fputs("\n\n", tmp);
a_ct++;
id = id->next_art;
}
fclose(tmp);
message("Article(s) saved - press any key to continue");
}
}
/*------------------------ search through articles ------------------------*/
int search_message(char *fn, ART_ID *current, int forward)
{
int offset = 0, found = FALSE;
TEXT *tx;
LINES *text;
char prompt[128], pattern[128];
int irq = FALSE;
sprintf(prompt, "Search %s for [%s] ",
forward ? "forward" : "backward", search_text);
lmessage(prompt);
gets(pattern);
if (pattern[0] == 0x1b)
return(0);
if (strlen(pattern) > 0)
strcpy(search_text, pattern);
else
strcpy(pattern, search_text);
strlwr(pattern);
message("*** searching - please wait (press <ESC> to abort ***");
for (;;) {
current = forward ? current->next_art : current->prev_art;
if (current == NULL)
break;
offset++;
tx = load_article(fn, current->art_off, FALSE);
for (text=tx->start; !found && text!=NULL; text=text->next) {
strlwr(text->data);
found = (strstr(text->data, pattern) != NULL);
}
free_article(tx);
if (kbhit())
irq = (getch() == 27);
if (found || irq)
break;
}
if (!found) {
sprintf(prompt, "*** '%s' not found - press any key ***", pattern);
message(prompt);
getch();
}
return found ? offset : 0;
}
/*------------------------ read a thread ------------------------------*/
int read_thread(ACTIVE * gp, ARTICLE * this_thread, ART_ID * first, int a_ct)
{
ART_ID *id;
char *fn;
int idx, res;
TEXT *tx;
char author[128], msg_id[128], r_name[128];
CROSS_POSTS *h, *h0;
ACTIVE *gx;
int junk_flag; /* nz - going to junk group */
fn = make_news_group_name(gp->group);
id = first;
while (id != NULL) {
tx = load_article(fn, id->art_off, TRUE);
res = read_article(gp, tx, this_thread, a_ct, id);
if (res & EX_NOTREAD)
res &= ~EX_NOTREAD;
else {
/* mark this article as read */
idx = (int) ((id->id) - gp->lo_num - 1);
*((gp->read_list) + idx) = (char) TRUE;
/* mark the crossposts */
get_his_stuff(tx, author, msg_id, r_name);
if ((h0 = look_up_history(msg_id, gp->group)) != NULL) {
h = h0;
while (h != NULL) {
gx = find_news_group(h->group, &junk_flag);
idx = (int) ((h->art_num) - gx->lo_num - 1);
*((gx->read_list) + idx) = (char) TRUE;
h = h->next;
}
free_cross_post_list(h0);
h0 = NULL;
}
}
DBGOUT(("%s:%d\n", __FILE__, __LINE__));
free_article(tx);
tx = NULL;
switch (res) {
case EX_QUIT:
id = NULL;
case EX_NEXT_UNREAD:
while (id != NULL) {
idx = (int) (id->id - gp->lo_num - 1);
if (*((gp->read_list) + idx) == FALSE) {
break;
}
a_ct++;
id = id->next_art;
}
if (id == NULL)
message("-- No more articles in thread --");
break;
case EX_SEARCH_FORW:
res = search_message(fn, id, 1);
while (res--) {
a_ct++;
id = id->next_art;
}
break;
case EX_SEARCH_BACKW:
res = search_message(fn, id, 0);
while (res--) {
a_ct--;
id = id->prev_art;
}
break;
case EX_PREV:
if (id->prev_art) {
id = id->prev_art;
a_ct--;
}
break;
case EX_PREVIOUS:
a_ct--;
id = id->prev_art;
break;
case EX_FIRST:
while (id->prev_art != NULL) {
id = id->prev_art;
a_ct--;
}
break;
case EX_LAST:
while (id->next_art != NULL) {
id = id->next_art;
a_ct++;
}
break;
default: /* EX_NEXT */
a_ct++;
id = id->next_art;
break;
} /* switch (res) */
}
return (res);
}
/*--------------------- smarter subject compare --------------------------*/
int strip_off_part(char *str)
{
char *ptr = str + strlen(str);
/* strip off (case-insensitive) things like:
* - "Part01/10"
* - "Part 01/10"
* - "Part 01 of 10"
* - "[1/10]"
* - "(1 of 10)"
* - "1 of 10"
* - "Patch02a/04"
* - "Patch20"
*/
while ( ptr > str && ptr[-1] == ' ' )
ptr--;
if ( ptr > str && (ptr[-1] == ')' || ptr[-1] == ']') )
ptr--;
while ( ptr > str && isdigit(ptr[-1]) )
ptr--;
if ( !isdigit(*ptr) )
return 0;
if ( ptr > str && ptr[-1] == '/' )
ptr--;
if ( ptr > str && ptr[-1] == '.' )
ptr--;
else if ( ptr > str + 3 && strnicmp(ptr - 4, " of ", 4) == 0 )
ptr -= 4;
else if ( ptr > str + 4 && strnicmp(ptr - 5, "Patch", 5) == 0 ) {
ptr -= 5;
goto label;
}
else if ( ptr > str + 5 && strnicmp(ptr - 6, "Patch ", 6) == 0 ) {
ptr -= 6;
goto label;
}
else
return 0;
if ( ptr > str && 'a' <= ptr[-1] && ptr[-1] <= 'z' )
ptr--;
while ( ptr > str && isdigit(ptr[-1]) )
ptr--;
if ( !isdigit(*ptr) )
return 0;
if ( ptr > str && (ptr[-1] == '(' || ptr[-1] == '[') )
ptr--;
while ( ptr > str && ptr[-1] == ' ' )
ptr--;
if ( ptr > str + 3 && strnicmp(ptr - 4, "Part", 4) == 0 )
ptr -= 4;
label:
while ( ptr > str && ptr[-1] == ' ' )
ptr--;
if ( ptr > str && ptr[-1] == ',' )
ptr--;
else if ( ptr > str && ptr[-1] == ':' )
ptr--;
*ptr = 0;
return 1;
}
/*------------------------------------------------------------------------*/
char *skip_vi(char *str)
{
char *ptr = str;
/* skip things like "v02i0027: " */
while ( isspace(*ptr) )
ptr++;
if ( *ptr++ != 'v' )
return str;
if ( !isdigit(*ptr) )
return str;
while ( isdigit(*ptr) )
ptr++;
if ( *ptr++ != 'i' )
return str;
if ( !isdigit(*ptr) )
return str;
while ( isdigit(*ptr) )
ptr++;
if ( *ptr++ != ':' )
return str;
if ( *ptr++ != ' ' )
return str;
while ( isspace(*ptr) )
ptr++;
return ptr;
}
/*------------------------------------------------------------------------*/
int smartcmp(char *str1, char *str2)
{
int slen;
char s1[256], s2[256];
strcpy(s1, str1);
strcpy(s2, str2);
if (strip_off_part(s1) && strip_off_part(s2)) {
str1 = skip_vi(s1);
str2 = skip_vi(s2);
}
slen = (int) (strlen(str1) < strlen(str2))
? strlen(str1) : strlen(str2);
if (my_stuff.match_len == -1)
if (strlen(str1) <= strlen(str2))
return -1;
else
return 1;
if (my_stuff.match_len == 0)
slen = 128;
if (--slen < my_stuff.match_len)
slen = my_stuff.match_len;
return (strnicmp(str1, str2, slen));
}
/*------------------------- read the headers --------------------------*/
ARTICLE *get_headers(ACTIVE * gp)
{
/*
* Read the files and get the headers
*/
char *fn;
char buf[512], fnx[256], *buf_p;
long g, n_read;
FILE *tmp_file;
ARTICLE *start, *that, *tmp;
ARTICLE **ptr;
ART_ID *art_this, *art_new;
int ct_art, cmp;
n_read = 0;
ct_art = 0;
start = NULL;
fn = make_news_group_name(gp->group);
sprintf(fnx, "%s.IDX", fn);
if ((tmp_file = fopen(fnx, "rb")) != NULL) {
for (g = gp->lo_num + 1; g <= gp->hi_num; g++) {
if ((n_read++ % 10) == 0) {
gotoxy(1, scr_rows);
printf("%d articles processed", n_read - 1);
}
/*
* Read the index Search the linked list for the subject
* - allocate a new subject if necessary
* - add to the existing list
*/
if (fgets(buf, 255, tmp_file) == NULL) {
gotoxy(1, scr_rows);
fprintf(stderr, "\nsnews: index file is corrupt (%s)\n", gp->group);
exit(1);
}
/* check all is in sync */
if (g != atol(buf + 9)) {
gotoxy(1, scr_rows);
fprintf(stderr, "\nsnews: in %s article %ld found when %ld"
" expected\n", gp->group, atol(buf + 9), g);
exit(1);
}
/* skip the two eight digit numbers and the 9 and the spaces */
buf_p = buf + 28;
eat_gunk(buf_p);
for (tmp=start; tmp != NULL; ) {
cmp = smartcmp(buf_p, tmp->header);
if (cmp > 0) {
if (tmp->left)
tmp = tmp->left;
else {
ptr = &(tmp->left);
tmp = NULL;
break;
}
}
else if (cmp < 0) {
if (tmp->right)
tmp = tmp->right;
else {
ptr = &(tmp->right);
tmp = NULL;
break;
}
}
else {
/* found this subject */
tmp->num_articles++;
/* allocate new article */
art_new = (ART_ID *) malloc(sizeof(ART_ID));
art_new->id = g;
art_new->art_off = atol(buf);
/* place it at end */
art_this = tmp->art_num;
while (art_this->next_art != NULL) {
art_this = art_this->next_art;
}
art_this->next_art = art_new;
art_new->prev_art = art_this;
art_new->next_art = NULL;
break;
}
}
if (tmp == NULL) {
/* not found - allocate new thread */
if (start == NULL) {
start = (ARTICLE *) malloc(sizeof(ARTICLE));
that = start;
that->last = NULL;
}
else {
ct_art++;
that->next = (ARTICLE *) malloc(sizeof(ARTICLE));
that->next->last = that;
that = that->next;
*ptr = that;
}
that->next = NULL;
that->index = ct_art;
that->left = that->right = NULL;
/* store article data */
strcpy(that->header, buf_p);
that->num_articles = 1;
that->art_num = (ART_ID *) malloc(sizeof(ART_ID));
that->art_num->prev_art = NULL;
that->art_num->next_art = NULL;
that->art_num->id = g;
that->art_num->art_off = atol(buf);
}
}
fclose(tmp_file);
}
else {
gotoxy(1, scr_rows);
fprintf(stderr, "\nsnews: can't open index file %s\n", fnx);
exit(1);
}
gp->threads = ct_art+1;
return (start);
}
/*------------------------ clean up subject line ----------------------------*/
void eat_gunk(char *buf)
{
/*
* This routine take the header line, and strips the word
* header word, 'Re:', and any extra blanks, trim to 59
* chars
*/
char *p, tmp[256];
buf[58] = 0;
/* strip the Subject: etc off the front */
/* while ((strstr(buf, "Re:") != NULL) ||
(strstr(buf, "re:") != NULL) ||
(strstr(buf, "RE:") != NULL) || */
while ((strnicmp(buf, "re:", 3) == 0) ||
(strnicmp(buf, "From:", 5) == 0) ||
(strnicmp(buf, "Path:", 5) == 0) ||
(strnicmp(buf, "Subject:", 8) == 0) ||
(strnicmp(buf, "Organization:", 13) == 0) ||
(strnicmp(buf, "Organisation:", 13) == 0) ||
(strnicmp(buf, "Followup-To:", 12) == 0)) {
p = strstr(buf, ":");
strcpy(buf, p + 1);
}
strcpy(tmp, buf);
*buf = '\x00';
p = strtok(tmp, " \t\n\r");
while (p != NULL) {
if (stricmp(p, "Re:") != 0) {
strcat(buf, p);
strcat(buf, " ");
}
else {
if (strlen(buf) > 0) {
strcat(buf, p);
strcat(buf, " ");
}
}
p = strtok(NULL, " \t\n\r");
}
if (strlen(buf) <= 1)
strcpy(buf, "** no subject **");
else
buf[strlen(buf)-1] = '\0'; /* remove trailing space */
}
/*-------------------- release the subject structures ---------------------*/
void free_header(ARTICLE * start)
{
/*
* Work our way through the subject structure releasing all the memory
*/
ARTICLE *a, *t;
ART_ID *u, *v;
a = start;
while (a != NULL) {
u = a->art_num;
while (u != NULL) {
v = u->next_art;
free(u);
u = v;
}
t = a->next;
free(a);
a = t;
}
}
/*------------------- count unread articles in a thread --------------------*/
int count_unread_in_thread(ACTIVE * gp, ARTICLE * a)
{
/*
* Take the thread 'a' for the given group 'gp' and return the number
* of unread articles
*/
ART_ID *id;
int ct, idx;
ct = 0;
id = a->art_num;
while (id != NULL) {
idx = (int) (id->id - gp->lo_num - 1);
if (*((gp->read_list) + idx) == FALSE)
ct++;
id = id->next_art;
}
return (ct);
}
/*------------------- count unread articles in a group --------------------*/
int count_unread_in_group(ACTIVE * gp)
{
/*
* Take the thread 'a' for the given group 'gp' and return the number
* of unread articles
*/
int articles, ct, i;
ct = 0;
articles = (int) (gp->hi_num - gp->lo_num);
for (i = 0; i < articles; i++) {
if (*((gp->read_list) + i) == FALSE)
ct++;
}
return (ct);
}
/*------------------- mark unread articles in a group --------------------*/
int mark_group_as_read(ACTIVE * gp, int ask)
{
/*
* Take the given group 'gp' and mark all the unread articles
* as read.
*/
int articles, i, ch;
if ((ask == TRUE) && (my_stuff.expert != TRUE)) {
message("Mark all articles in group as read (y/n) [y] ? ");
do
{
ch = getch();
ch = tolower(ch);
}
while ((ch != 'y') && (ch != 'n') && (ch != 'q') && (ch != ESCAPE) && (ch != '\r'));
} else
ch = 'y';
if ((ch == 'y') || (ch == '\r')) {
articles = (int) (gp->hi_num - gp->lo_num);
for (i = 0; i < articles; i++)
*((gp->read_list) + i) = (char) TRUE;
return TRUE;
}
return FALSE;
}
/*----------------- mark read articles in a group as unread ----------------*/
void mark_group_as_unread(ACTIVE * gp)
{
/*
* Take the given group 'gp' and mark all the read articles as
* unread.
*/
int articles, i, ch;
if (my_stuff.expert != TRUE) {
message("Mark all articles in group as unread (y/n) [y] ? ");
do
{
ch = getch();
ch = tolower(ch);
}
while ((ch != 'y') && (ch != 'n') && (ch != 'q') && (ch != ESCAPE) && (ch != '\r'));
}
else
ch = 'y';
if ((ch == 'y') || (ch == '\r')) {
articles = (int) (gp->hi_num - gp->lo_num);
for (i = 0; i < articles; i++)
*((gp->read_list) + i) = (char) FALSE;
}
}
/*-------------------------- print a thread ---------------------------------*/
void print_thread(ACTIVE *gp, ARTICLE *this_thread)
{
ART_ID *id;
char *fn;
int a_ct;
FILE *tmp_file;
char buf[512];
message("*** printing Article(s) - please wait ***");
fn = make_news_group_name(gp->group);
id = this_thread->art_num;
a_ct = 0;
while (id != NULL) {
if ((tmp_file = fopen(fn, "rb")) != NULL) {
fseek(tmp_file, id->art_off, SEEK_SET);
while (fgets(buf, sizeof(buf), tmp_file) != NULL) {
if (strncmp(buf, "@@@@END\n", 7) == 0)
break;
fputs(buf, stdprn);
fputc(0x0d, stdprn);
}
fputc(0x0c, stdprn);
fflush(stdprn);
}
a_ct++;
id = id->next_art;
}
}
/*-------------------------- status message ---------------------------------*/
void message(char *msg)
{
int x;
gotoxy(1, scr_rows - 1);
textbackground(msgb);
textcolor(msgf);
clreol();
x = (scr_cols - strlen(msg)) / 2;
gotoxy(x, scr_rows - 1);
cprintf("%s", msg);
textbackground(textb);
textcolor(textf);
}
/*-------------------------- status message ---------------------------------*/
void lmessage(char *msg)
{
gotoxy(1, scr_rows - 1);
textbackground(msgb);
textcolor(msgf);
clreol();
cprintf("%s", msg);
textbackground(textb);
textcolor(textf);
}
/*-------------------------- status message ---------------------------------*/
void command(char *msg)
{
int x;
gotoxy(1, scr_rows);
textbackground(msgb);
textcolor(msgf);
clreol();
x = (scr_cols - strlen(msg)) / 2;
gotoxy(x, scr_rows);
cprintf(msg);
textbackground(textb);
textcolor(textf);
}
#ifndef ATARI
/****************************************************************************
* system *
* Replacement for the standard system () function. *
* Error code added MSM 3 Jun 93 *
*****************************************************************************/
int system(const char *cmd)
{
char *p, *q; /* -> command and parameters */
unsigned int rc; /* return value */
char prompt[80]; /* Error message buffer */
if (strlen(cmd) == 0) {
rc = do_exec("", "", USE_ALL, 0xffff, NULL);
return(rc);
}
p = strdup(cmd);
if (p == NULL)
return (0x102); /* no memory */
q = p;
while (*q != ' ' && *q != '\t' && q != '\0')
q++;
if (*q != '\0') {
*q = '\0';
q++; /* q -> parameters */
}
rc = do_exec(p, q, USE_ALL, 0xffff, NULL);
if (rc > (unsigned) 0xff) {
sprintf(prompt, "Unable to run command \"%s %s\"", p, q);
message(prompt);
getch();
}
#if defined (free)
#undef free /* in case of debugging package! */
#endif
free(p);
return (rc);
} /* int system (const char *cmd) */
#ifndef __TURBOC__
unsigned long farcoreleft(void)
{
union _REGS inregs, outregs;
_heapmin();
inregs.h.ah = 0x48;
inregs.x.bx = 0xffff;
_intdos(&inregs, &outregs);
return (16l * (long)outregs.x.bx);
}
unsigned long testcoreleft(void)
{
union _REGS inregs, outregs;
inregs.h.ah = 0x48;
inregs.x.bx = 0xffff;
_intdos(&inregs, &outregs);
return (16l * (long)outregs.x.bx);
}
#endif
#endif